package com.bertie.smiles2iupac;

import java.util.ArrayList;
import java.util.List;

public class Molecule {
    private List<Atom> atomsArray = new ArrayList<Atom>();

    private Molecule(String smilesFormat) {
    	buildFromSmiles(smilesFormat);
    }

	public static Molecule buildFromSmilesFormatString(String smilesFormatString) {
		// Validate
		String smilesFormat = smilesFormatString.trim().replaceAll("\\s", "") .toUpperCase();
		if (smilesFormat.replaceAll("[C()]", "").length() > 0) {
			throw new IllegalArgumentException("Unsupported SMILES format : " + smilesFormatString);
		}
		return new Molecule(smilesFormat);
	}

    public List<AtomsChain> getAllChains() {
        List<AtomsChain> chains = new ArrayList<AtomsChain>();
        for (Atom leaf : getLeaves()) {
        	traverseAndAddChains(new AtomsChain(), leaf, chains);
        }
        return chains;
    }

	public static int findStartOfParenthesis(String s, int endParenthesisPosition) {
		int nestingCounter = 0;
		for (int j = endParenthesisPosition; j >= 0; j--) {
			switch (s.charAt(j)) {
			case ')':
				nestingCounter++;
				break;
			case '(':
				nestingCounter--;
				break;
			}
			if(nestingCounter==0){
				return j;
			}
		}
		throw new IllegalArgumentException("BAD FORMAT - Unmatched brackets: '" + s + "':" + endParenthesisPosition);
    }

	private Atom buildFromSmiles(String smilesFormatString) {
		Atom atom = null;
		for (int i = smilesFormatString.length() - 1; i >= 0; i--) {
			if (atom == null || smilesFormatString.charAt(i) == ')') {
				if (smilesFormatString.charAt(i) == ')') {
					List<Atom> adjacentAtoms = new ArrayList<Atom>();
					if (atom != null) {
						adjacentAtoms.add(atom);
					}
					int startParenPos = findStartOfParenthesis(smilesFormatString, i);
					String subexpression = smilesFormatString.substring(startParenPos + 1, i);
					Atom headAtom = buildFromSmiles(subexpression);
					adjacentAtoms.add(headAtom);
					i = startParenPos - 1;
					if (smilesFormatString.charAt(i) == ')') {
						startParenPos = findStartOfParenthesis(smilesFormatString, i);
						subexpression = smilesFormatString.substring(startParenPos + 1, i);
						headAtom = buildFromSmiles(subexpression);
						adjacentAtoms.add(headAtom);
						i = startParenPos - 1;
					}
					atom = new Atom(adjacentAtoms);
				} else {
					atom = new Atom();
				}
			} else {
				List<Atom> adjacentAtoms = new ArrayList<Atom>();
				adjacentAtoms.add(atom);
				atom = new Atom(adjacentAtoms);
			}
			atomsArray.add(atom);
		}
		return atom;
	}  

    @SuppressWarnings("serial")
    private List<Atom> getLeaves() {
        return new ArrayList<Atom>() {
            {
                for (Atom atom : atomsArray) {
                    if (atom.getAdjacentAtoms().size() < 2) {
                        add(atom);
                    }
                }
            }
        };
    }

    private void traverseAndAddChains(AtomsChain currentChain, Atom currentAtom, List<AtomsChain> chains) {
        List<Bond> bondsToNewAtoms = new ArrayList<Bond>(currentAtom.getBonds());
        if (currentChain.getAtoms().size() > 0) {
            Atom lastAtom = currentChain.lastAtom();
            for (Bond bond : currentAtom.getBonds()) {
                if (bond.getAtomAtOtherEnd(currentAtom) == lastAtom) {
                    bondsToNewAtoms.remove(bond);
                }
            }
        }
        currentChain.add(currentAtom);
        if (bondsToNewAtoms.size() == 0) {
            chains.add(currentChain);
        } else {
            for (Bond bond : bondsToNewAtoms) {
                traverseAndAddChains(currentChain.copy(), bond.getAtomAtOtherEnd(currentChain.lastAtom()), chains);
            }
        }
    }
}
